#include <pthread.h>
#include <iostream>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <vector>
#include <thread>
#include "Thread.hpp"
#include "LockGuard.hpp"
using namespace std;

// 模拟买票
int g_tickets = 10000; // 共享资源，没有保护的S

class ThreadData
{
public:
    ThreadData(string &name, int total, int &_tickets, pthread_mutex_t& mutex)
        : _name(name), _total(total), _tickets(_tickets), _mutex(mutex)
    {
    }
    ~ThreadData()
    {
    }

public:
    string _name;
    int _total;
    int &_tickets;
    pthread_mutex_t& _mutex; //每个线程申请的都是同一个锁
};

/*1.多线程访问临界前，需要将临界资源保护起来，保护临界资源的本质是保护临界区(访问临界资源的代码)！！！
  2.加锁可以保护临界资源，将临界区"锁起来"。本质就是让多线程以串行的方执行被保护的代码，只有一个线程在执行完临界区后，其他线程才能执行
  3.加锁是一种让多线程"互斥"
  4.定义为全局或static的锁需要以宏PTHREAD_MUTEX_INITIALIZER初始化,定义为局部的锁，需要通过pthread_mutex_init()函数初始化
  5.初始化完成后，就可以对通过函数对临界区进行保护了。保护临界区是通过申请锁完成的：一个锁，只允许一个线程进行使用，其他线程申请已经被使用的锁时，会被阻塞
  6.使用完锁后，需要让该线程对锁进行解锁，当所有线程都不需要该锁时，需要对锁进行释放
  6.申请锁:pthread_mutex_lock() 解锁:pthread_mutex_unlock() 释放锁:pthread_mutex_destroy()
  7.加锁的粒度要越细越好 -- 加锁代码尽可能少

*/


// 申请一个全局锁
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;


void route(ThreadData *td)
{
    // 加锁
    while (true)
    {
        LockGuard mutex(td->_mutex);    //通过RAII特性，利用局部变量的生命周期进行使用锁、释放锁

        //pthread_mutex_lock(&td->_mutex);    //第一个启动的线程才能加锁
        if (td->_tickets > 0)
        {
            usleep(1000);
            printf("%s get tickets: %d\n", td->_name.c_str(), td->_tickets);
            td->_tickets--;
            //pthread_mutex_unlock(&td->_mutex);   
            td->_total++;
        }
        else
        {
            //pthread_mutex_unlock(&td->_mutex);   
            break;
        }
    }
}



// void route(ThreadData *td)
// {
//     // 加锁
//     while (true)
//     {
//         pthread_mutex_lock(&mutex);  //加锁是自由竞争的，竞争锁能力强的线程可能会让其他线程无法加锁，造成"饥饿问题"

//         if (td->_tickets > 0)
//         {
//             usleep(1000);
//             printf("%s get tickets: %d\n", td->_name.c_str(), td->_tickets);
//             td->_tickets--;
//             pthread_mutex_unlock(&mutex);   
//             td->_total++;
//         }
//         else
//         {
//             pthread_mutex_unlock(&mutex);   
//             break;
//         }
//     }
// }

const int num = 4;
int main()
{
    // std::cout << "main： &tickets: " << &g_tickets << std::endl;

    vector<ThreadModule::Thread<ThreadData *>> threads;
    vector<ThreadData *> datas;

      //申请局部锁
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, nullptr);

    // 1. 创建一批线程
    for (int i = 0; i < num; i++)
    {
        std::string name = "thread-" + std::to_string(i + 1);
        ThreadData *td = new ThreadData(name, 0, g_tickets, mutex);
        threads.emplace_back(route, td, name);
        datas.emplace_back(td);
    }

  
    // 2. 启动一批线程
    for (auto &thread : threads)
    {
        thread.Start();
    }

    // 3. 等待一批线程
    for (auto &thread : threads)
    {
        thread.Join();
        // std::cout << "wait thread done, thread is: " << thread.name() << std::endl;
    }

    // 4.查看每个线程的抢票情况
    for (auto td : datas)
    {
        printf("%s : tickets %d\n", td->_name.c_str(), td->_total);
        delete td;
    }

    return 0;
}
